﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace Se.Common
{
    /// <summary>
    /// Multi-Key Dictionary Class
    /// 多键字典
    /// </summary>
    public class MultiKeyDictionary<K, L, V>
    {
        internal readonly Dictionary<K, V> baseDictionary = new Dictionary<K, V>();
        internal readonly Dictionary<L, K> subDictionary = new Dictionary<L, K>();
        internal readonly Dictionary<K, L> primaryToSubkeyMapping = new Dictionary<K, L>();
        ReaderWriterLockSlim readerWriterLock = new ReaderWriterLockSlim();

        public V this[K key]
        {
            get
            {
                V item;
                if (baseDictionary.TryGetValue(key, out item))
                {
                    return item;
                }
                throw new KeyNotFoundException("sub key not found:" + key.ToString());
            }
        }

        public V this[L lKey]
        {
            get
            {
                K key;
                if (this.subDictionary.TryGetValue(lKey, out key))
                {
                    V item;
                    if (this.baseDictionary.TryGetValue(key, out item))
                    {
                        return item;
                    }
                }
                throw new KeyNotFoundException("sub key not found:" + key.ToString());
            }
        }


        public bool TryGetValue(K key, out V item)
        {
            item = default(V);
            this.readerWriterLock.EnterReadLock();
            try
            {
                if (this.baseDictionary.TryGetValue(key, out item))
                {
                    return true;
                }
                return false;
            }
            finally
            {
                this.readerWriterLock.ExitReadLock();
            }
        }

        public bool TryGetValue(L lKey, out V item)
        {
            item = default(V);
            this.readerWriterLock.EnterReadLock();
            try
            {
                K key;
                if (this.subDictionary.TryGetValue(lKey, out key))
                {
                    return this.baseDictionary.TryGetValue(key, out item);
                }
            }
            finally
            {
                this.readerWriterLock.ExitReadLock();
            }
            return false;
        }

        public void Add(K primaryKey, L subKey, V value)
        {
            this.readerWriterLock.EnterWriteLock();
            try
            {
                this.baseDictionary[primaryKey] = value;
                this.subDictionary[subKey] = primaryKey;
                this.primaryToSubkeyMapping[primaryKey] = subKey;
            }
            finally
            {
                this.readerWriterLock.ExitWriteLock();
            }
        }

        public void Remove(K primaryKey)
        {
            this.readerWriterLock.EnterWriteLock();
            try
            {
                this.baseDictionary.Remove(primaryKey);
                L subKey;
                if (this.primaryToSubkeyMapping.TryGetValue(primaryKey, out subKey))
                {
                    this.primaryToSubkeyMapping.Remove(primaryKey);
                    this.subDictionary.Remove(subKey);
                }
            }
            finally
            {
                this.readerWriterLock.ExitWriteLock();
            }
        }


        public void Remove(L subKey)
        {
            this.readerWriterLock.EnterWriteLock();
            try
            {
                K primaryKey;
                if (this.subDictionary.TryGetValue(subKey, out primaryKey))
                {
                    this.subDictionary.Remove(subKey);
                    this.baseDictionary.Remove(primaryKey);
                    this.primaryToSubkeyMapping.Remove(primaryKey);
                }
            }
            finally
            {
                this.readerWriterLock.ExitWriteLock();
            }
        }

        public bool ContrainKey(K key)
        {
            this.readerWriterLock.EnterReadLock();
            try
            {
                return this.baseDictionary.ContainsKey(key);
            }
            finally
            {
                this.readerWriterLock.ExitReadLock();
            }
        }

        public bool ContrainSubKey(L subKey)
        {
            this.readerWriterLock.EnterReadLock();
            try
            {
                return this.subDictionary.ContainsKey(subKey);
            }
            finally
            {
                this.readerWriterLock.ExitReadLock();
            }
        }

        public K[] Keys
        {
            get
            {
                this.readerWriterLock.EnterReadLock();
                try
                {
                    return this.baseDictionary.Keys.ToArray();
                }
                finally
                {
                    this.readerWriterLock.ExitReadLock();
                }
            }
        }

        public V[] Values
        {
            get
            {
                this.readerWriterLock.EnterReadLock();
                try
                {
                    return this.baseDictionary.Values.ToArray();
                }
                finally
                {
                    this.readerWriterLock.ExitReadLock();
                }
            }
        }

        public L[] SubKeys
        {
            get
            {
                this.readerWriterLock.EnterReadLock();
                try
                {
                    return this.subDictionary.Keys.ToArray();
                }
                finally
                {
                    this.readerWriterLock.ExitReadLock();
                }
            }
        }
    }
}
